package com.example.minioservice.minio.controller;

import io.minio.GetObjectArgs;
import io.minio.MinioClient;
import io.minio.StatObjectArgs;
import io.minio.StatObjectResponse;
import lombok.SneakyThrows;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRange;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.StreamUtils;
import org.springframework.util.unit.DataSize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.util.List;

@Controller
public class MinioController {
    @Resource
    private MinioClient minioClient;



    @SneakyThrows
    @ResponseBody
    @GetMapping("/download/{bucketName}/{objectName}")
    public ResponseEntity<StreamingResponseBody> download(
            HttpServletResponse response,
            @RequestHeader(name = HttpHeaders.RANGE, required = false) String httpRangesStr,
            @PathVariable String bucketName,
            @PathVariable String objectName) {


        StatObjectResponse stat = minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build());

        List<HttpRange> ranges = HttpRange.parseRanges(httpRangesStr);

        if (ranges.isEmpty()) {
            InputStream inputStream = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());
            return ResponseEntity.status(HttpStatus.OK).headers(headers -> {
                headers.addAll(HttpHeaders.ACCEPT_RANGES, stat.headers().toMultimap().get(HttpHeaders.ACCEPT_RANGES));
                headers.add(HttpHeaders.CONTENT_TYPE, stat.contentType());
                headers.remove(HttpHeaders.CONNECTION);
                headers.add(HttpHeaders.ETAG, String.format("\"%s\"", stat.etag()));
                headers.setDate(HttpHeaders.LAST_MODIFIED, stat.lastModified().toEpochSecond());
            }).body(outputStream -> StreamUtils.copy(inputStream, outputStream));
        }

        // 分段长度(可以从配置获取)
        long partLength = DataSize.parse("1M").toBytes();

        final long rangeStart = ranges.get(0).getRangeStart(0);
        long rangeEnd = Math.min(ranges.get(0).getRangeEnd(0), stat.size());
        if (rangeEnd <= 0) rangeEnd = Math.min(stat.size(), rangeStart + partLength);
        final long rangeLength = rangeEnd - rangeStart;

        if(rangeStart >= stat.size()) {
            return ResponseEntity.status(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE).build();
        }

        InputStream inputStream = rangeLength > 0 ? minioClient.getObject( GetObjectArgs.builder().bucket(bucketName).object(objectName).offset(rangeStart).length(rangeLength).build()) : null;
        long finalRangeEnd = rangeEnd;

        response.reset();
        return ResponseEntity.status(HttpStatus.PARTIAL_CONTENT).headers(headers -> {
            headers.addAll(HttpHeaders.ACCEPT_RANGES, stat.headers().toMultimap().get(HttpHeaders.ACCEPT_RANGES));
            headers.add(HttpHeaders.CONTENT_LENGTH, String.valueOf(rangeLength));
            headers.add(HttpHeaders.CONTENT_RANGE, String.format("bytes %d-%d/%d", rangeStart, finalRangeEnd - 1, stat.size()));
            headers.add(HttpHeaders.CONTENT_TYPE, stat.contentType());
            headers.add(HttpHeaders.ETAG, String.format("\"%s\"", stat.etag()));
            headers.setDate(HttpHeaders.LAST_MODIFIED, stat.lastModified().toEpochSecond());
        }).body(outputStream -> {
            if (inputStream != null) StreamUtils.copy(inputStream, outputStream);
        });
    }
}
